﻿#ifndef _PLAYER_H_
#define _PLAYER_H_
//作为玩家基类

#include"particle.h"
#include"camera.h"
#include"vector2.h"
#include"animation.h"
#include"player_id.h"
#include<graphics.h>
#include"platform.h"
#include<iostream>
#include "bullet.h"
#include "status_bar.h"


extern bool is_debug;
extern std::vector<Platform>platform_list;
extern std::vector<Bullet*>bullet_list;


extern IMAGE img_1P_cursor;
extern IMAGE img_2P_cursor;

extern Atlas atlas_run_effect;
extern Atlas atlas_jump_effect;
extern Atlas atlas_land_effect;


class Player
{
public:
	Player()
	{		
		current_animation = &animation_idle_right;

		timer_attack_cd.set_wait_time(attack_cd);
		timer_attack_cd.set_one_shot(true);
		timer_attack_cd.set_callback([&]() 
			{
			can_attack = true;
			});

		timer_invulnerable.set_wait_time(750);
		timer_invulnerable.set_one_shot(true);
		timer_invulnerable.set_callback([&]()
			{
				is_invulnerable = false;
			});

		timer_invulnerable_blink.set_wait_time(75);
		timer_invulnerable_blink.set_callback([&]()
			{
				is_showing_sketch_frame = !is_showing_sketch_frame;
			});

		timer_run_effect_generation.set_wait_time(75);
		timer_run_effect_generation.set_callback([&]()
			{
				Vector2 particle_position;
				IMAGE* frame = atlas_run_effect.get_image(0);
				particle_position.x = position.x + (size.x - frame->getwidth()) / 2;
				particle_position.y = position.y + size.y - frame->getheight();
				particle_list.emplace_back(particle_position, &atlas_run_effect, 45);//45存活时间

			});

		timer_die_effect_generation.set_wait_time(35);
		timer_die_effect_generation.set_callback([&]()
			{
				Vector2 particle_position;
				IMAGE* frame = atlas_run_effect.get_image(0);
				particle_position.x = position.x + (size.x - frame->getwidth()) / 2;
				particle_position.y = position.y + size.y - frame->getheight();
				particle_list.emplace_back(particle_position, &atlas_run_effect, 150);//150存活时间

			});


	}
	~Player() = default;

	virtual void on_run(float distance)
	{
		if (is_attacking_ex)//判断是否正在特殊攻击，若是则退出函数
			return;

		position.x += distance;					//移动
		timer_run_effect_generation.resume();	//使跑动特效定时器重新恢复运行的代码

	}

	virtual void on_jump()					//跳跃
	{
		if (velocity.y != 0||is_attacking_ex)		//防止实现连跳，只有当等于0时，才能再次跳跃
			return;


		velocity.y += jump_velocity;
	}

	virtual void on_land()
	{
		is_land_effect_visible = true;
		animation_land_effect.reset();

		//————特效生成位置————
		IMAGE* effect_frame = animation_land_effect.get_frame();
		position_land_effect.x = position.x + (size.x - effect_frame->getwidth()) / 2;
		position_land_effect.y = position.y + size.y - effect_frame->getheight();


	}

	virtual void on_update(int delta)
	{
		int direction = is_right_key_down - is_left_key_down;			//可通过二者作差的方式判断玩家此时的移动方向

		if (direction!=0)//当玩家没有移动时角色的朝向
		{
			if(!is_attacking_ex)
				is_facing_right= direction >= 0;
			current_animation = is_facing_right ? &animation_run_right : &animation_run_left;//玩家操作时的移动动画
			float distance = direction * run_velocity * delta;//dirction表示方向；run_velocity速度；delta时间；记录在某个方向上delta时间跑了多少距离
			on_run(distance);
		}
		else
		{
			current_animation = is_facing_right ? &animation_idle_right : &animation_idle_left;//玩家没有操作时的动画
			timer_run_effect_generation.pause();//当玩家不跑动时，粒子特效暂停生成
		}

		if (is_attacking_ex)
			current_animation = is_facing_right ? &animation_attack_ex_right : &animation_attack_ex_left;

		//————死亡时击飞动画————
		if (hp <= 0)
			current_animation = last_hurt_direction.x < 0 ? &animation_die_left : &animation_die_right;

		

		current_animation->on_update(delta);		//对当前的动画进行更新
		animation_jump_effect.on_update(delta);		//跳跃动画的更新代码
		animation_land_effect.on_update(delta);		//落地动画更新

		timer_attack_cd.on_update(delta);
		timer_invulnerable.on_update(delta);
		timer_invulnerable_blink.on_update(delta);
		timer_run_effect_generation.on_update(delta);//跑动时的粒子特效触发方法

		if (hp <= 0)
			timer_die_effect_generation.on_update(delta);

		//更新粒子列表中的对象，使用和子弹更新逻辑相似的代码,使用remove_if移除失效的粒子
		particle_list.erase(std::remove_if(
			particle_list.begin(), particle_list.end(),
			[](const Particle& particle)
			{
				return !particle.check_valid();
			}),
			particle_list.end());

		//接着遍历剩余的粒子列表，依次调用on_update
		for (Particle& particle : particle_list)
			particle.on_update(delta);


		if (is_showing_sketch_frame)//判断是否需要剪影，若需要则将当前动画的当前帧抓取出来
		{
			sketch_image(current_animation->get_frame(), &img_sketch);

		}


		move_and_collide(delta);	//实现玩家下坠

	}

	virtual void on_draw(const Camera& camera)
	{
		if (is_jump_effect_visible)
			animation_jump_effect.on_draw(camera, (int)position_jump_effect.x, (int)position_jump_effect.y);

		if (is_land_effect_visible)
			animation_land_effect.on_draw(camera, (int)position_land_effect.x, (int)position_land_effect.y);

		for (Particle& particle : particle_list)
			particle.on_draw(camera);

		if (hp > 0 && is_invulnerable && is_showing_sketch_frame)
			putimage_alpha(camera, (int)position.x, (int)position.y,&img_sketch);
		else
			current_animation->on_draw(camera, (int)position.x, (int)position.y);
		
		if (is_cursor_visible)//光标可见时，显示的位置
		{
			switch (id)
			{
			case PlayerID::P1:
				putimage_alpha(camera, (int)(position.x + (size.x - img_1P_cursor.getwidth()) / 2),
					(int)(position.y - img_1P_cursor.getheight()), &img_1P_cursor);
				break;
			case PlayerID::P2:
				putimage_alpha(camera, (int)(position.x + (size.x - img_2P_cursor.getwidth()) / 2),
					(int)(position.y - img_2P_cursor.getheight()), &img_2P_cursor);
				break;
			}
		}

		if (is_debug)
		{
			setlinecolor(RGB(0, 125, 255));
			rectangle((int)position.x, (int)position.y, (int)(position.x + size.x), (int)(position.y + size.y));
		}
	}

	virtual void on_input(const ExMessage& msg)
	{
		switch (msg.message)
		{
		case WM_KEYDOWN:
			switch (id)
			{
			case PlayerID::P1:
				switch (msg.vkcode)
				{
					//'A'
				case 0x41:
					is_left_key_down = true;
					break;

					//'D'
				case 0x44:
					is_right_key_down = true;
					break;
					
					//'W'
				case 0x57:
					on_jump();
					break;

					//'F'
				case 0x46:
					if (can_attack)
					{
						on_attack();
						can_attack = false;
						timer_attack_cd.restart();
					}
					break;

					//'G'
				case 0x47:
					if (mp >= 100)
					{
						on_attack_ex();
						mp = 0;
					}
					break;
				}
				break;
			case PlayerID::P2:
				switch (msg.vkcode)
				{
					//'⬅'
				case VK_LEFT:
					is_left_key_down = true;
					break;
					
					//'→‘
				case VK_RIGHT:
					is_right_key_down = true;
					break;

					//'⬆'
				case VK_UP:
					on_jump();
					break;

					//'.'
				case VK_OEM_PERIOD:
					if (can_attack)
					{
						on_attack();
						can_attack = false;
						timer_attack_cd.restart();
					}
					break;

					//'/'
				case VK_OEM_2:
					if (mp >= 100)
					{
						on_attack_ex();
						mp = 0;
					}
					break;
				}
				break;
			}
			break;

		case WM_KEYUP:
			switch (id)
			{
			case PlayerID::P1:
				switch (msg.vkcode)
				{
					//'A'
				case 0x41:
					is_left_key_down = false;
					break;

					//'D'
				case 0x44:
					is_right_key_down = false;
					break;
				}
				break;
			case PlayerID::P2:
				switch (msg.vkcode)
				{
					//'⬅'
				case VK_LEFT:
					is_left_key_down = false;
					break;

					//'→‘
				case VK_RIGHT:
					is_right_key_down =false;
					break;
				}
				break;
			}
			break;
		default:
			break;
		}
	
	
	}

	virtual void on_attack(){}

	virtual void on_attack_ex(){}

	void set_idle_direction(bool idle_dirction)
	{
		switch (id)
		{
		case PlayerID::P1:
			is_facing_right = idle_dirction;
			break;
		case PlayerID::P2:
			is_facing_right = idle_dirction;
			break;
		}
	}


	void set_id(PlayerID id)				//用来在玩家对象构造时对玩家的id进行赋值
	{
		this->id = id;
	}

	void set_position(float x, float y)
	{
		position.x = x, position.y = y;
	}

	const Vector2& get_position()const
	{
		return position;
	}

	const Vector2& get_size()const
	{
		return size;
	}

	void set_hp(int val)
	{
		hp = val;
	}

	int get_hp()const
	{
		return hp;
	}

	int get_mp()const
	{
		return mp;
	}

	void make_invulnerable()//触发无敌状态的方法
	{
		is_invulnerable = true;
		timer_invulnerable.restart();

	}




protected:
	void move_and_collide(int delta)//物理相关的所有代码都写在这里
	{

		//想要触发落地特效，需要前一帧下落速度！=0，后一帧=0
		
		float last_velocity_y = velocity.y;//记录前一帧速度


		//这两行代码即可实现重力的模拟
		velocity.y+= gravity * delta;			//重力加速度下对玩家的速度进行累加
		position += velocity * (float)delta;	//再根据玩家的速度值，对玩家的位置进行更新

		if (hp < 0)//防止玩家死亡被击飞时撞击上其它物体
			return;


		//实现单向平台碰撞
		//由于玩家可以从平台下方跳跃到平台上，所以和平台的碰撞只需要检测，玩家在下坠时，也就是玩家速度向下时再进行检测就可以了
		if (velocity.y > 0)
		{
			for (const Platform& platform : platform_list)
			{
				const Platform::CollisionShape& shape = platform.shape;//获取平台的碰撞外形

				//最右边界的值-最左边界的值<=矩形宽度+线段长度
				bool is_collide_x = (max(position.x + size.x, shape.right) - min(position.x, shape.left) <= size.x + (shape.right - shape.left));

				bool is_collide_y = (shape.y >= position.y && shape.y <= position.y + size.y);

				if (is_collide_x && is_collide_y)
				{
					float delta_pos_y = velocity.y * delta;	//在水平和竖直都发生碰撞时，计算得到在这一帧过程中，玩家在竖直方向上移动的距离
					float last_tick_foot_pos_y = position.y + size.y - delta_pos_y;//计算上一帧玩家脚底的位置，
													//可看作时回退先前对position的修改,我们就可以判断在发生碰撞的前一帧玩家是否整个穿过平台
					if (last_tick_foot_pos_y <= shape.y)
					{
						position.y = shape.y - size.y;
						velocity.y = 0;//当前帧下落速度=0；

						if (last_velocity_y != 0)//前一帧！=0
							on_land();//触发落地特效


						//为了不必要的判断，在判定成功后使用break退出循环
						break;
					}

				}

			}
		}

		if (!is_invulnerable)//首先判断玩家不处于无敌状态
		{
			//进行子弹和敌方玩家的碰撞实现
			for (Bullet* bullet : bullet_list)//遍历子弹列表
			{
				if (!bullet->get_valid() || bullet->get_collide_target() != id)//判断子弹是否有效，碰撞目标是否对方
					continue;//如果不是则跳过

				if (bullet->check_collision(position, size))//如果能够碰撞
				{
					make_invulnerable();
					bullet->on_collide();//执行碰撞函数
					bullet->set_valid(false);//碰撞过后的子弹设为失效
					hp -= bullet->get_damage();
					last_hurt_direction = bullet->get_position() - position;//子弹位置和玩家位置作差，更新最后一次受击方向
					if (hp <= 0)
					{
						velocity.x = last_hurt_direction.x<0 ? 0.35f : -0.35f;
						velocity.y = -1.0f;
					}

				}
			}
		}
		
		


	}
 

	
protected:
	Vector2 position;						//用于记录玩家在世界地图中的坐标信息


	Animation animation_idle_left;			//朝向左的默认动画
	Animation animation_idle_right;			//朝向右的默认动画
	Animation animation_run_left;			//朝向左的奔跑动画
	Animation animation_run_right;			//朝向右的奔跑动画

	Animation animation_attack_ex_left;		//朝向左的特殊攻击动画
	Animation animation_attack_ex_right;	//朝向右的特殊攻击动画

	bool is_jump_effect_visible = false;	//跳跃动画是否可见
	bool is_land_effect_visible = false;	//落地动画是否可见

	Vector2 position_jump_effect;			//跳跃动画播放位置,用来描述特效位置
	Vector2 position_land_effect;			//落地动画播放位置

	Animation* current_animation = nullptr; //当前正在播放的动画

	PlayerID id = PlayerID::P1;				//玩家序号

	bool is_left_key_down = false;			//向左移动按键是否按下；
											//之前在提瓦特幸存者就发现，直接对玩家坐标进行累加使角色移动效果不好，使用bool类型间接触发
	bool is_right_key_down = false;			//向右移动按键是否按下；

	bool is_facing_right = true;			//用来标识玩家角色朝向；
											//由于玩家不进行操作时，角色动画会保持当前的状态不会进行任何翻转操作，
											// 所以不能直接使用当前的移动方向判断角色动画的朝向



	Timer timer_attack_cd;					//普通攻击冷却定时器

	bool can_attack = true;					//是否可以释放普通攻击

	bool is_invulnerable = false;			//角色是否处于无敌状态

	bool is_showing_sketch_frame = false;	//当前帧是否应该显示剪影（就是角色受伤后的白色身影）

	Timer timer_invulnerable;				//无敌状态定时器
	Timer timer_invulnerable_blink;			//无敌状态闪烁定时器

	IMAGE img_sketch;						//动画帧剪影图片



	int attack_cd = 500;					//普通攻击冷却时间（毫秒）

	bool is_attacking_ex = false;			//是否正在释放特殊攻击（特殊攻击期间玩家无法移动）



	int mp = 0;								//玩家能量
	int hp = 100;							//玩家生命值



protected:
	//这里是实现物理引擎
	const float gravity = 1.6e-3f;			//玩家重力

	const float run_velocity = 0.55f;		//跑动速度

	const float jump_velocity = -0.85f;		//跳跃速度

	Vector2 velocity;						//记录角色当前速度

	Vector2 size;							//记录玩家的尺寸，用以实现玩家的矩形碰撞箱
											//需要在具体子类的构造函数中赋值（peashooter.h、sunflower.h）
										 
											
	//——————粒子特效——————
	
	std::vector<Particle>particle_list;		//粒子对象数组；存储和当前玩家有关的粒子对象

	Timer timer_run_effect_generation;		//跑动特效粒子发射定时器
	Timer timer_die_effect_generation;		//死亡特效粒子发射定时器


	//————光标————
	bool is_cursor_visible = true;			//玩家光标指示性是否显示
	Timer timer_cursor_visible;				//玩家光标指示器可见性定时器

	//————玩家死亡后的击飞动画————
	Vector2 last_hurt_direction;			//最后一次受击的方向


};



#endif // !_PLAYER_H_

